from os.path import getsize, split, join, abspath, isdir
from os import listdir
from sha import sha
from string import strip
from time import time
import math
from BitTorrent.bencode import bencode, bdecode
from pprint import pprint

class torrentfile:

    def __init__(self):
        self.info = {}
        pass

    def make(self, file, trackerurl, comment = None, target = None):
        self.trackerurl = trackerurl
        self.comment = comment
        self.__name = file
        
        file = abspath(file)
        if isdir(file):
            subs = subfiles(file)
            subs.sort()
            pieces = []
            sh = sha()
            done = 0
            fs = []
            totalsize = 0.0
            totalhashed = 0
            for p, f in subs:
                totalsize += getsize(f)

            piece_length = choose_piece_length(size)
            self.__size = totalsize
            
            for p, f in subs:
                pos = 0
                size = getsize(f)
                fs.append({'length': size, 'path': p})
                h = open(f, 'rb')
                while pos < size:
                    a = min(size - pos, piece_length - done)
                    sh.update(h.read(a))
                    if flag.isSet():
                        return
                    done += a
                    pos += a
                    totalhashed += a

                    if done == piece_length:
                        pieces.append(sh.digest())
                        done = 0
                        sh = sha()
                h.close()
            if done > 0:
                pieces.append(sh.digest())
            self.info = {'pieces': ''.join(pieces),
                         'piece length': piece_length, 'files': fs, 
                         'name': split(file)[1]}
        else:
            size = getsize(file)
            self.__size = size
            piece_length = choose_piece_length(size)
            pieces = []
            p = 0
            h = open(file, 'rb')
            while p < size:
                x = h.read(min(piece_length, size - p))
                pieces.append(sha(x).digest())
                p += piece_length
                if p > size:
                    p = size
            h.close()

            self.info = {'pieces': ''.join(pieces), 
                         'piece length': piece_length, 'length': size, 
                         'name': split(file)[1]}
    
    def bin(self):
        data = {'info': self.info, 'announce': strip(self.trackerurl), 'creation date': long(time())}
        if self.comment:
            data['comment'] = self.comment
        return bencode(data)

    def write(self, file):
        fh = open(file, 'wb')
        fh.write(self.bin())
        fh.close()

    def read(self, filename):
        fh = open(filename, 'rb')
        bin = fh.read()
        fh.close()

        data = bdecode(bin)
        pprint(data)
        self.__name = filename
        self.info = data['info']
        self.trackerurl = data['announce']
        if data.has_key('comment'):
            self.comment = data['comment']
        else:
            self.comment = None
        self.__size = self.info['length']

    def size(self):
        return self.__size

    def name(self):
        return self.__name

    def hash(self):
        return sha(bencode(self.info)).hexdigest()


def choose_piece_length(size):
    num_pieces_pow2 = 11 # 2**num_pieces_pow2 = desired number of pieces
    minimum_piece_len_pow2 = 18
    return 2**max(minimum_piece_len_pow2, int(round(math.log(size, 2)) - num_pieces_pow2))
    
def calcsize(file):
    if not isdir(file):
        return getsize(file)
    total = 0
    for s in subfiles(abspath(file)):
        total += getsize(s[1])
    return total


def subfiles(d):
    r = []
    stack = [([], d)]
    while len(stack) > 0:
        p, n = stack.pop()
        if isdir(n):
            for s in listdir(n):
                if s not in ignore and s[:1] != '.':
                    stack.append((copy(p) + [s], join(n, s)))
        else:
            r.append((p, n))
    return r
